﻿using System;
using System.Collections.Generic;
using System.Linq;
using Ewk.MachineLearning.Classification.Conversions;
using Ewk.Math.Algebra;
using Ewk.Math.ComputationTypes;

namespace Ewk.MachineLearning.Clustering.MultidimensionalScaling
{
    public class EntityDifferenceContainer<TVectorElement> : DifferenceContainer<TVectorElement>,
                                                             IEntityDifferenceContainer<TVectorElement>
        where TVectorElement : struct, IComparable, IFormattable, IConvertible, IComparable<TVectorElement>,
            IEquatable<TVectorElement>
    {
        private readonly string[] _entityIds;

        /// <summary>
        /// Initializes a new instance.
        /// </summary>
        /// <param name="entityType">The <see cref="Type"/> of the entity.</param>
        /// <param name="calculationFunction">The function that calculates the difference.</param>
        /// <param name="vectors">The <see cref="EntityVector{T}"/> list to calculate difference angles for.</param>
        public EntityDifferenceContainer(
            Type entityType,
            Func<Vector<TVectorElement>, Vector<TVectorElement>, Computable<TVectorElement>> calculationFunction,
            params EntityVector<TVectorElement>[] vectors)
            : base(calculationFunction, vectors.Select(v => v.Vector).ToArray())
        {
            EntityType = entityType;
            _entityIds = vectors.Select(v => v.EntityId).ToArray();
        }

        /// <summary>
        /// Gets a unique identifier for the i-th <see cref="EntityVector{T}"/> of the internal list.
        /// </summary>
        /// <param name="i">The index of the <see cref="EntityVector{T}"/> within the internal list.</param>
        /// <returns>The i-th <see cref="EntityVector{T}"/> of the internal list.</returns>
        public string this[int i]
        {
            get { return _entityIds[i]; }
        }

        /// <summary>
        /// Gets the differences between entities.
        /// </summary>
        public IEnumerable<EntityDifferences<TVectorElement>> EntityDifferences
        {
            get
            {
                var entityDifferencesList = new List<EntityDifferences<TVectorElement>>();

                for (var i = 0; i < VectorCount;i++ )
                {
                    var entityDifferences = new EntityDifferences<TVectorElement>(this[i]);
                    entityDifferencesList.Add(entityDifferences);

                    for (var j = i + 1; j < VectorCount;j++ )
                    {
                        var entityDifference = new EntityDifference<TVectorElement>(this[j], this[i, j].Value);
                        entityDifferences.Add(entityDifference);
                    }
                }

                return entityDifferencesList;
            }
        }

        /// <summary>
        /// Gets the type of the Entity that is represented by <see cref="EntityVector{T}"/> instances.
        /// </summary>
        public Type EntityType { get; private set; }
    }
}